#if 1 //################################### Headers ###############################################################//
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <smlib>
#include <ctfmod/ctfmod_core>

#undef REQUIRE_PLUGIN 
#include <autoupdate>

#define PLUGIN_VERSION "0.0.1"
#define DEBUG 1

public Plugin:myinfo = {
	name = "CTFmod Core",
	author = "KawMAN",
	description = "Capture The Flag Core for SourceMOD",
	version = PLUGIN_VERSION,
	url = "http://www.wsciekle.pl/"
};
#endif // ###################### END HEADER ##############################################################################//

#if 1 //################################### Globalss ##############################################################//
//Forwards
new Handle:g_TurnOnFwd				= INVALID_HANDLE;
new Handle:g_TurnOffFwd				= INVALID_HANDLE;
new Handle:g_OnObjectTouch			= INVALID_HANDLE;
new Handle:g_OnObjectCapture		= INVALID_HANDLE;
new Handle:g_OnObjectTake			= INVALID_HANDLE;
new Handle:g_OnObjectDrop			= INVALID_HANDLE;

//Defaults
const MAXOBJECTS					= DMAXOBJECTS;
new String:g_MdlDir[]				= "models/";

new Float:g_Def_Pos[3]						= {0.0, ... };
new Float:g_Def_Ang[3]						= {0.0, ... };
new Float:g_Def_PlrPos[3]					= {30.0, -7.0 , 0.0 };
new Float:g_Def_PlrAng[3]					= {0.0, 240.0 , 50.0 };
new Float:g_Def_PosCorect[3]				= {0.0, 0.0 , 15.0 }; //Position corection when using sm_ctf_add without position
new String:g_Def_AttachPoint[MAXCVARLEN]	= "primary";
new String:g_Def_Animation[MAXCVARLEN]		= "";

new String:g_Def_Model[MAXTEAMS][MAXCVARLEN];
new String:g_Def_ModelBase[MAXTEAMS][MAXCVARLEN];


//Cvars
new Handle:g_Cvar_Enable			= INVALID_HANDLE;
new Handle:g_Cvar_Events			= INVALID_HANDLE;
new bool:g_Enable					= false;
new bool:g_Events					= false;

//ObjectTypes
new String:g_ObjectTypes[MAXOBJECTS][32];
new g_ObjectTypesF[MAXOBJECTS]	= {0, ...};
new g_ObjectTypesCount 					= 0;

//Base Types
new g_Type_Flag							= 0;
new g_Type_Base							= 0;

// Objects Setting
new g_ObjEnt[MAXOBJECTS]				= {0, ...};		// Current Object EntId
new g_ObjTeam[MAXOBJECTS]				= {0, ...};		// Object team
new g_ObjType[MAXOBJECTS]				= {0, ...};		// Type of Object (flag, base, other?), 0 = NoObject Defined
new g_ObjOwner[MAXOBJECTS]				= {0, ...};		// Who has the object, 0 = nobody
new Float:g_ObjPos[MAXOBJECTS][3];						// Normal stand position (flag base)
new Float:g_ObjAng[MAXOBJECTS][3];						// Standing angles (in base & free)
new Float:g_ObjPlrAng[MAXOBJECTS][3];					// Angles when on player
new Float:g_ObjPlrPos[MAXOBJECTS][3];					// Pos when on player
new String:g_ObjModel[MAXOBJECTS][MAXCVARLEN];			// Model for object
new String:g_ObjAttachPoint[MAXOBJECTS][MAXCVARLEN];	
new String:g_ObjAnimation[MAXOBJECTS][MAXCVARLEN];	

new g_ObjectsCount						= 0;			// Store added object count

#endif

#if 1 //*********************************** SourceMOD Fwd *********************************************************//
public OnPluginStart()
{
	LoadTranslations("ctfmod.phrases");
	
	g_Cvar_Enable		= CreateConVar("ctf_enable", "1", "CTFmod Core state.");
	g_Cvar_Events		= CreateConVar("ctf_events", "1", "Fire Core Events.");
	
	HookConVarChange(g_Cvar_Enable, MyCvarChange);
	
	g_Def_Model[RED] = "models/mapmodels/flags2.mdl";
	g_Def_Model[BLU] = "models/mapmodels/flags.mdl";
	g_Def_Model[BOTH_TEAMS] = "models/mapmodels/pole.mdl";
	g_Def_ModelBase[RED] = "models/props_trainstation/TrackSign02.mdl";
	g_Def_ModelBase[BLU] = "models/props_trainstation/TrackSign02.mdl";
	g_Def_ModelBase[BOTH_TEAMS] = "models/props_trainstation/TrackSign02.mdl";
	
	AddObjType("null", 0);
	g_Type_Flag = AddObjType("flag", CTFTYPE_MUST_ALIVE | CTFTYPE_ALLOWTOUCH_OTHERTEAM | CTFTYPE_ALLOWTAKE_OTHERTEAM | CTFTYPE_ALLOWCAPTURE);
	g_Type_Base = AddObjType("base", CTFTYPE_MUST_ALIVE | CTFTYPE_ALLOWTOUCH_TEAM | CTFTYPE_CAPTURE_OTHERTEAM);
	
	RegAdminCmd("sm_ctf_add", Cmd_CtfAdd, ADMFLAG_ROOT, "Set falg/base positions");
	RegAdminCmd("sm_ctf_defset", Cmd_CtfDefSet, ADMFLAG_ROOT, "Set default values");
	RegAdminCmd("sm_ctf_clear", Cmd_CtfClear, ADMFLAG_ROOT, "Clear objects setup");
	RegAdminCmd("sm_ctf_test_attachpos", Cmd_CtfTestAttachPoint, ADMFLAG_ROOT, "Test pos & ang settings, you need to grab object");
	RegAdminCmd("sm_ctf_print_obj", Cmd_CtfPrintObj, ADMFLAG_ROOT, "Print current Objects setup");
	
	AutoExecConfig(true, "ctfmod/ctfmod");
	CreateConVar("sm_ctf_version", PLUGIN_VERSION, "Capture The Flag mod version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
	
	//Events
	HookEvent("round_start", Event_RoundStart);
	HookEvent("player_death", Event_PlayerDeath);
	
	//Global Forwards
	g_TurnOnFwd = CreateGlobalForward("OnCTFTurnOn", ET_Hook);
	g_TurnOffFwd = CreateGlobalForward("OnCTFTurnOff", ET_Hook);
	
	
	//Priv Forwards
	g_OnObjectTouch		= CreateForward(ET_Hook, Param_CellByRef, Param_CellByRef, Param_String, Param_FloatByRef);
	g_OnObjectCapture	= CreateForward(ET_Hook ,Param_CellByRef, Param_CellByRef, Param_CellByRef);
	g_OnObjectTake		= CreateForward(ET_Hook ,Param_CellByRef, Param_CellByRef);
	g_OnObjectDrop		= CreateForward(ET_Hook ,Param_CellByRef, Param_CellByRef);
	
	
	RefreshSetting();
}

public OnPluginEnd()
{
	TurnOff();
}

public OnMapStart()
{
	RefreshSetting();
}

public OnMapEnd()
{
	//TurnOff(); //1 Part of restarter on mapchange
}
#endif

#if 1 //################################### Help Commands #########################################################//
public Action:Cmd_CtfReCreate(client, args)
{
	RemoveAllObjects();
	if(!g_Enable) 
	{
		ReplyToCommand(client, "CTFMod Disabled can't create objects");
	}
	else
	{
		CreateAllObjects();
	}
	
	return Plugin_Handled;
}
public Action:Cmd_CtfPrintObj(client, args)
{
	ReplyToCommand(client,"g_ObjectsCount: %d", g_ObjectsCount);
	for(new i =0; i<g_ObjectsCount; i++)
	{
		ReplyToCommand(client,"ObjId:%d Type:%d - E:%d T:%d O:%d A:%s M:%s", i, g_ObjType[i], g_ObjEnt[i], g_ObjTeam[i], g_ObjOwner[i], g_ObjAttachPoint[i], g_ObjModel[i] );
	}
	return Plugin_Handled;
}

public Action:Cmd_CtfTestAttachPoint(client, args) 
{
	decl String:arg[128], Float:pos[3], Float:ang[3];
	
	GetCmdArg(1, arg, sizeof(arg));
	pos[0] = StringToFloat(arg);
	GetCmdArg(2, arg, sizeof(arg));
	pos[1] = StringToFloat(arg);
	GetCmdArg(3, arg, sizeof(arg));
	pos[2] = StringToFloat(arg);
	GetCmdArg(4, arg, sizeof(arg));
	ang[0] = StringToFloat(arg);
	GetCmdArg(5, arg, sizeof(arg));
	ang[1] = StringToFloat(arg);
	GetCmdArg(6, arg, sizeof(arg));
	ang[2] = StringToFloat(arg);
	
	new Obj = Array_FindValue(g_ObjOwner, g_ObjectsCount, client);
	if(Obj <= -1) 
	{
		//TODO:
		return Plugin_Handled;
	}
	TeleportEntity(g_ObjEnt[Obj], pos, ang, NULL_VECTOR);
	return Plugin_Handled;
}

#endif

#if 1 //################################### Events ################################################################//

public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
	CreateAllObjects();
}
public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{	
	new client = GetClientOfUserId(GetEventInt(event, "userid"));
	new Obj = GetClientObject(client);
	if(Obj != -1) DropObject(Obj, client);
}



#endif

#if 1 //################################### Objects Types #########################################################//
AddObjType(String:name[], flags)
{
	decl mytype;
	if(g_ObjectTypesCount >= MAXOBJECTS)
	{
		return 0; //Too many Types.
	}
	if( (mytype = Array_FindString(g_ObjectTypes, g_ObjectTypesCount, name, false)) != -1)
	{
		return mytype;
	}
	strcopy(g_ObjectTypes[g_ObjectTypesCount], sizeof(g_ObjectTypes[]), name);
	g_ObjectTypesF[g_ObjectTypesCount] = flags;
	mytype = g_ObjectTypesCount;
	g_ObjectTypesCount++; 
	return mytype;
	//return g_ObjectTypesCount++; //Should increment after return
}
stock RemoveObjType(String:name[])
{
	RemoveObjTypeId(Array_FindString(g_ObjectTypes, g_ObjectTypesCount, name, false));
}
stock bool:RemoveObjTypeId(type)
{
	if(type <= 0)
	{
		return false;
	}
	for(new i = type; i < g_ObjectTypesCount; i++)
	{
		strcopy(g_ObjectTypes[i], sizeof(g_ObjectTypes[]), g_ObjectTypes[i+1]);
	}
	g_ObjectTypesCount--; 
	return true;
}
#endif

#if 1 //################################### Objects Controlers ####################################################//

//Fired after Parenting to setup positions nad angles
public ParentDelay(const String:output[], caller, activator, Float:delay)
{
	new Obj = ObjEntToObj(caller);
	TeleportEntity(caller, g_ObjPlrPos[Obj], g_ObjPlrAng[Obj], NULL_VECTOR); 
}

//Fired when Object Touched
public ObjectTouchHook(const String:output[], caller, activator, Float:delay)
{
	decl Action:result;
	decl Handle:event;
	if(GetForwardFunctionCount(g_OnObjectTouch) > 0)
	{
		Call_StartForward(g_OnObjectTouch);
		Call_PushCellRef(caller);
		Call_PushCellRef(activator);
		Call_PushString(output);
		Call_PushFloatRef(delay);
		Call_Finish(_:result);
		if(result >= Plugin_Handled) return;
	}
	
	new Obj = ObjEntToObj(caller);
	
	//Event
	event = CreateEvent("ctf_touched");
	if (event == INVALID_HANDLE)
	{
		return;
	}
	SetEventInt(event, "userid", GetClientUserId(activator));
	SetEventInt(event, "touchedent", caller);
	SetEventString(event, "touchedtype", g_ObjectTypes[g_ObjType[Obj]]);
	FireEvent(event);
	
	if (Obj <= -1) return; //Unknown Object
	
	if(activator > 0 && CanTouch(activator, Obj))
	{ //Player can interact with object
		new ClientObj = GetClientObject(activator);
		if(ClientObj >= 0 && CanCapture(Obj, ClientObj))
		{ //Player touched Base 		and 		have some object							and		can capture this object
			result = Plugin_Continue;
			if(GetForwardFunctionCount(g_OnObjectCapture) > 0)
			{
				Call_StartForward(g_OnObjectCapture);
				Call_PushCellRef(activator);
				Call_PushCellRef(Obj);
				Call_PushCellRef(ClientObj);
				Call_Finish(_:result);
			}
			if(result < Plugin_Handled)	
			{
				//Event
				event = CreateEvent("ctf_captured");
				if (event == INVALID_HANDLE)
				{
					return;
				}
				SetEventInt(event, "userid", GetClientUserId(activator));
				SetEventInt(event, "capent", caller);
				SetEventString(event, "captype", g_ObjectTypes[g_ObjType[Obj]]);
				SetEventInt(event, "capturedent", g_ObjEnt[ClientObj]);
				SetEventString(event, "capturedtype", g_ObjectTypes[g_ObjType[ClientObj]]);
				FireEvent(event);
				CaptureObj(activator, Obj, ClientObj);
			}
			if(result >= Plugin_Stop)	return;
		}
		
		if(CanTake(activator, Obj) ) //Player touched flag & He dont have objects
		{
			if(GetForwardFunctionCount(g_OnObjectTake) > 0)
			{
				Call_StartForward(g_OnObjectTake);
				Call_PushCellRef(activator);
				Call_PushCellRef(Obj);
				Call_Finish(_:result);
				if(result >= Plugin_Handled) return;
			}
			//Event
			event = CreateEvent("ctf_taken");
			if (event == INVALID_HANDLE)
			{
				return;
			}
			SetEventInt(event, "userid", GetClientUserId(activator));
			SetEventInt(event, "takenent", caller);
			SetEventString(event, "takentype", g_ObjectTypes[g_ObjType[Obj]]);
			FireEvent(event);
			GiveObject(activator, Obj);
			return;
		}
	}
	
	//Player cant interact with object, but object is valid so TurnOn beam again
	SetVariantString("");
	AcceptEntityInput(caller, "FireUser4"); //Restart Beam Trigger
}

CaptureObj(client, Obj, ClientObj)
{
	//TODO: Event fire & forward
	ReturnObject(ClientObj);
}

GiveObject(client, Obj)
{
	//TODO: Event fire & forward
	if(g_ObjEnt[Obj] > 0 && IsClientConnected(client) && IsClientInGame(client))
	{
		g_ObjOwner[Obj] = client;
		SetVariantString("");
		AcceptEntityInput(g_ObjEnt[Obj], "FireUser2", client, g_ObjEnt[Obj]); //Parent Object (Give)
	}
}

bool:DropObject(Obj, client=-1)
{
	decl Action:result;
	if(GetForwardFunctionCount(g_OnObjectDrop) > 0)
	{
		Call_StartForward(g_OnObjectDrop);
		Call_PushCellRef(Obj);
		Call_PushCellRef(client);
		Call_Finish(_:result);
		if(result >= Plugin_Handled) return false;
	}
	if(Obj >= 0 && Obj <= g_ObjectsCount && g_ObjEnt[Obj] > 0 && g_ObjOwner[Obj] > 0)
	{
		g_ObjOwner[Obj] = 0;
		if(IsValidEntity(g_ObjEnt[Obj]))
		{
			Entity_RemoveParent(g_ObjEnt[Obj]);
			TeleportEntity(g_ObjEnt[Obj], NULL_VECTOR, g_ObjAng[Obj], NULL_VECTOR);
			SetVariantString("");
			AcceptEntityInput(g_ObjEnt[Obj], "FireUser4"); //Restart Beam Trigger
		}
		return true;
	}
	return false;
}

//Teleport Object to base position
ReturnObject(Obj)
{
	if(Obj >= 0 && Obj <= g_ObjectsCount && g_ObjEnt[Obj] > 0 )
	{
		g_ObjOwner[Obj] = 0;
		if(IsValidEntity(g_ObjEnt[Obj]))
		{
			Entity_RemoveParent(g_ObjEnt[Obj]);
			TeleportEntity(g_ObjEnt[Obj], g_ObjPos[Obj], g_ObjAng[Obj], NULL_VECTOR);
			SetVariantString("");
			AcceptEntityInput(g_ObjEnt[Obj], "FireUser4"); //Restart Beam Trigger
		}
	}
}

stock bool:DropObjectByEnt(ObjEnt)
{
	new Obj = ObjEntToObj(ObjEnt);
	return DropObject(Obj);
}

stock DropClientObjects(client)
{
	decl Obj, c;
	c = 0;
	while( ( Obj = Array_FindValue(g_ObjOwner, g_ObjectsCount, client) ) != -1)
	{
		DropObject(Obj, client);
		++c;
	}
	return c;
}

#endif

#if 1 //################################### Objects Creators/Destroyers ###########################################//

CreateAllObjects() // Create all objects from config
{
	decl Ent;
	for(new i = 0; i < g_ObjectsCount; i++)
	{
		if(g_ObjType[i] == 0) continue; //Must have type
		
		Ent = g_ObjEnt[i] = CreateObject(i);
		if(Ent <= -1)
		{
			//TODO: Log error
			continue;
		}
		if(strlen(g_ObjAnimation[i]) > 1) 
		{
			SetVariantString(g_ObjAnimation[i]);
			AcceptEntityInput(Ent, "SetDefaultAnimation");
			SetVariantString(g_ObjAnimation[i]);
			AcceptEntityInput(Ent, "SetAnimation");
		}
		CreateTrigger(Ent);
	}
}

RemoveAllObjects() //Remove all objects from map (this in config and non in config but created by core)
{
	decl Ent;
	for(new i = 0; i < g_ObjectsCount; i++)
	{
		Ent = g_ObjEnt[i];
		if(Ent > 0 && IsValidEntity(Ent)) //Ent exist ?
		{
			EntKillH(Ent); //KillIt
		}
		g_ObjEnt[i] = 0;
		g_ObjOwner[i] = 0;
	}
	//TODO: Kill object by name using engine
}

CreateTrigger(parent)
{
	decl ent, String:ParentName[MAXCVARLEN], String:myName[MAXCVARLEN], String:tmp[MAXCVARLEN], Float:pos[3], Float:endpos[3];
	if(parent <= 0) return 0;
	//End pos of beam
	endpos[0] = 0.0;
	endpos[1] = 0.0;
	endpos[2] = 100.0;
	
	Entity_GetName(parent, ParentName, sizeof(ParentName));
	Entity_GetAbsOrigin(parent, pos);
	ent = CreateEntityByName("env_beam");
	if(ent <= -1) return ent;
	
	Format(myName, sizeof(myName), "CTFMTrig_%d_%d", parent, ent); //generate object name with entid
	DispatchKeyValue(ent, "targetname", myName);
	DispatchKeyValue(ent, "LightningStart", myName);
	DispatchKeyValue(ent, "LightningEnd", ParentName);
	DispatchKeyValue(ent, "texture", "sprites/laserbeam.spr");
	DispatchKeyValue(ent, "TouchType", "4"); // Trigger on touched by players or NPC
	DispatchKeyValueFloat(ent, "BoltWidth", 20.0);
	DispatchKeyValue(ent, "life", "0");
	DispatchKeyValue(ent, "rendercolor", "0 0 255");
	DispatchKeyValue(ent, "renderamt", "0"); // Invisible beam
	DispatchKeyValue(ent, "HDRColorScale", "1.0");
	DispatchKeyValue(ent, "decalname", "Bigshot");
	DispatchKeyValue(ent, "StrikeTime", "0");
	DispatchKeyValue(ent, "TextureScroll", "35");
	Format(tmp, sizeof(tmp), "%s,FireUser1,,0,-1", ParentName);
	DispatchKeyValue(ent, "OnTouchedByEntity", tmp);
	
	//Prepare self-restart mechanism
	DispatchKeyValue(ent, "OnUser4", "!self,TurnOff,,0,-1"); 
	DispatchKeyValue(ent, "OnUser4", "!self,TurnOn,,0.01,-1");
	
	
	SetEntPropFloat(ent, Prop_Data, "m_fWidth", 20.0);
	SetEntProp(ent, Prop_Data, "m_clrRender", 0); //renderamt
	SetEntPropVector(ent, Prop_Data, "m_vecEndPos", endpos);
	TeleportEntity(ent, pos, NULL_VECTOR, NULL_VECTOR); //Parten to flag so dont need
	SetEntityModel(ent, "sprites/laserbeam.spr");
	
	//Connect/Parent to Flag
	SetVariantString(ParentName);
	AcceptEntityInput(ent, "SetParent");
	
	// Add beam restart shortcut to Parent Object Ent
	Format(tmp, sizeof(tmp), "OnUser4 %s:FireUser4::0.1:-1", myName);
	SetVariantString(tmp);
	AcceptEntityInput(parent, "AddOutput");
	
	// To enable OnTouched...
	SetVariantString("");
	AcceptEntityInput(ent, "TurnOff"); 
	AcceptEntityInput(ent, "TurnOn");
	
	//ChangeEdictState(ent);
	return ent;
}
CreateObject(Obj)
{
	decl ent, String:tmp[MAXCVARLEN];
	
	ent = CreateEntityByName("prop_dynamic");
	
	if(ent <= -1) return ent;
	DispatchKeyValue(ent, "model", g_ObjModel[Obj]);
	Format(tmp, sizeof(tmp), "CTFMObject_%d", ent); //generate object name with entid
	DispatchKeyValue(ent, "targetname", tmp);
	DispatchKeyValue(ent, "classname", "prop_dynamic");
	DispatchKeyValue(ent, "rendermode", "0"); 
	DispatchKeyValue(ent, "renderfx", "0"); 
	DispatchKeyValue(ent, "renderamt", "255"); 
	DispatchKeyValue(ent, "rendercolor", "255 255 255");
	//DispatchKeyValue(ent, "DefaultAnim", "flag_idle1"); //Default Animation
	
	//Prepare parenting mechanism OnUser2, fired by ObjectTouchHook function (FireUser2)
	DispatchKeyValue(ent, "OnUser2", "!self,SetParent,!activator,0,-1");  // !self - This object, !activator - player touched beam
	Format(tmp, sizeof(tmp), "!self,SetParentAttachment,%s,0.01,-1", g_ObjAttachPoint[Obj]);
	DispatchKeyValue(ent, "OnUser2", tmp); // primary - Attach point on players model, 0.01 delay
	DispatchKeyValue(ent, "OnUser2", "!self,FireUser3,,0.05,-1"); // primary - Attach point on players model, Parent Delay
	
	TeleportEntity(ent, g_ObjPos[Obj], g_ObjAng[Obj], NULL_VECTOR);
	DispatchSpawn(ent);
	
	//Fired OnTouched... by beam. Hooed here because i want to have object(flag/base, not beam) entity id in 
	// ObjectTouchHook - caller arg
	HookSingleEntityOutput(ent, "OnUser1", ObjectTouchHook); 
	HookSingleEntityOutput(ent, "OnUser3", ParentDelay); 
	
	return ent;
}

#endif

#if 1 //################################### Objects setup commands ################################################//
PrintHelp_CtfAdd(client=0) 
{
	ReplyToCommand(client, "%t", "Help_CtfSet");
}

// TODO: Move this command to ctfmod_tools
public Action:Cmd_CtfDefSet(client, args) 
{
	decl String:arg[128], what;
	if(args<1)
	{
		//TODO: Help Output/Syntax
		//TODO: Print config ?
		return Plugin_Handled;
	}
	
	GetCmdArg(1, arg, sizeof(arg));
	what = DetermineWhat(arg);
	
	if(args >= 2) GetCmdArg(2, arg, sizeof(arg));
	switch(what)
	{
		case 0:
		{
			//TODO: Wrong syntax
			return Plugin_Handled;
		}
		case 1: //Model Flag Red
		{
			if( args >= 2 )
			{
				strcopy(g_Def_Model[RED], sizeof(g_Def_Model[]), arg);
				PrecacheModel(g_Def_Model[RED]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_Model[RED]);
			}
		}
		case 2: // Model Flag Blu
		{
			if( args >= 2 )
			{
				strcopy(g_Def_Model[BLU], sizeof(g_Def_Model[]), arg);
				PrecacheModel(g_Def_Model[BLU]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_Model[BLU]);
			}
		}
		case 3: //Pos
		{
			if( args >= 4 )
			{
				g_Def_Pos[0] = StringToFloat(arg);
				GetCmdArg(3, arg, sizeof(arg));
				g_Def_Pos[1] = StringToFloat(arg);
				GetCmdArg(4, arg, sizeof(arg));
				g_Def_Pos[2] = StringToFloat(arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %f %f %f", g_Def_Pos[0], g_Def_Pos[1], g_Def_Pos[2]);
			}
		}
		case 4: //Ang
		{
			if( args >= 4 )
			{
				g_Def_Ang[0] = StringToFloat(arg);
				GetCmdArg(3, arg, sizeof(arg));
				g_Def_Ang[1] = StringToFloat(arg);
				GetCmdArg(4, arg, sizeof(arg));
				g_Def_Ang[2] = StringToFloat(arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %f %f %f", g_Def_Ang[0], g_Def_Ang[1], g_Def_Ang[2]);
			}
		}
		case 5: //PlrAng
		{
			if( args >= 4 )
			{
				g_Def_PlrAng[0] = StringToFloat(arg);
				GetCmdArg(3, arg, sizeof(arg));
				g_Def_PlrAng[1] = StringToFloat(arg);
				GetCmdArg(4, arg, sizeof(arg));
				g_Def_PlrAng[2] = StringToFloat(arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %f %f %f", g_Def_PlrAng[0], g_Def_PlrAng[1], g_Def_PlrAng[2]);
			}
		}
		case 6: //Model Base Red
		{
			if( args >= 2 )
			{
			strcopy(g_Def_ModelBase[RED], sizeof(g_Def_ModelBase[]), arg);
			PrecacheModel(g_Def_ModelBase[RED]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_ModelBase[RED]);
			}
		}
		case 7: //Model Base Red
		{
			if( args >= 2 )
			{
				strcopy(g_Def_ModelBase[BLU], sizeof(g_Def_ModelBase[]), arg);
				PrecacheModel(g_Def_ModelBase[BLU]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_ModelBase[BLU]);
			}
		}
		case 8: //AttachPoint
		{
			if( args >= 2 )
			{
				strcopy(g_Def_AttachPoint, sizeof(g_Def_AttachPoint), arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_AttachPoint);
			}
		}
		case 9: // PlrPos
		{
			if( args >= 4 )
			{
				g_Def_PlrPos[0] = StringToFloat(arg);
				GetCmdArg(3, arg, sizeof(arg));
				g_Def_PlrPos[1] = StringToFloat(arg);
				GetCmdArg(4, arg, sizeof(arg));
				g_Def_PlrPos[2] = StringToFloat(arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %f %f %f", g_Def_PlrPos[0], g_Def_PlrPos[1], g_Def_PlrPos[2]);
			}
		}
		case 10: // ModelFlagBoth
		{
			if( args >= 2 )
			{
				strcopy(g_Def_Model[BOTH_TEAMS], sizeof(g_Def_Model[]), arg);
				PrecacheModel(g_Def_Model[BOTH_TEAMS]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_Model[BOTH_TEAMS]);
			}
		}
		case 11: // ModelBaseBoth
		{
			if( args >= 2 )
			{
				strcopy(g_Def_ModelBase[BOTH_TEAMS], sizeof(g_Def_ModelBase[]), arg);
				PrecacheModel(g_Def_ModelBase[BOTH_TEAMS]);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_ModelBase[BOTH_TEAMS]);
			}
		}
		case 12: // Default Animation
		{
			if( args >= 2 )
			{
				strcopy(g_Def_Animation, sizeof(g_Def_Animation), arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %s", g_Def_Animation);
			}
		}
		case 13: // Position Corection
		{
			if( args >= 4 )
			{
				g_Def_PosCorect[0] = StringToFloat(arg);
				GetCmdArg(3, arg, sizeof(arg));
				g_Def_PosCorect[1] = StringToFloat(arg);
				GetCmdArg(4, arg, sizeof(arg));
				g_Def_PosCorect[2] = StringToFloat(arg);
			}
			else
			{
				ReplyToCommand(client, "Current Value %f %f %f", g_Def_PosCorect[0], g_Def_PosCorect[1], g_Def_PosCorect[2]);
			}
		}
		
		default:
		{
					//TODO: Unknown syntax
			ReplyToCommand(client,"Wrong syntax");
		}
	}
	return Plugin_Handled;
}

public Action:Cmd_CtfClear(client, args) 
{
	decl i, String:arg[10];
	if(args>=1)
	{
		GetCmdArg(1, arg, sizeof(arg));
		i = StringToInt(arg);
		if(i > g_ObjectsCount) i = g_ObjectsCount;
	}
	else
	{
		i = g_ObjectsCount;
	}

	for(; i > 0 ; i--)
	{
		if(g_ObjType[g_ObjectsCount-1]<= 0) continue;
		CopyVector(g_ObjPos[g_ObjectsCount-1], g_Def_Pos);
		CopyVector(g_ObjAng[g_ObjectsCount-1], g_Def_Ang);
		CopyVector(g_ObjPlrAng[g_ObjectsCount-1], g_Def_PlrAng);
		CopyVector(g_ObjPlrPos[g_ObjectsCount-1], g_Def_PlrPos);

		//EntKillH(g_ObjEnt[g_ObjectsCount-1]);
		g_ObjEnt[g_ObjectsCount-1]		= 0;
		g_ObjType[g_ObjectsCount-1]		= 0;
		g_ObjTeam[g_ObjectsCount-1]		= 0;
		
		g_ObjectsCount--;
	}
	ReplyToCommand(client, "%s", "Deleted object sets");
	return Plugin_Handled;
}

public Action:Cmd_CtfAdd(client, args) 
{

	if (args < 2 ) // Print Help
	{
		PrintHelp_CtfAdd(client);
	}
	else
	{
		decl String:arg[128], type, team, String:MyModel[MAXCVARLEN], Float:objpos[3], Float:objang[3], Float:objplrang[3];
		decl Float:objplrpos[3], String:AttachPoint[MAXCVARLEN], String:Animation[MAXCVARLEN];
		
		// #### Get object type
		GetCmdArg(1, arg, sizeof(arg));
		type = Array_FindString(g_ObjectTypes, g_ObjectTypesCount, arg, false);
		if(type == -1) //unknown object type
		{
			ReplyToCommand(client, "%t", "Unknown object type", arg);
			PrintHelp_CtfAdd(client) ;
			return Plugin_Handled;
		}
		
		// #### Get object team
		GetCmdArg(2, arg, sizeof(arg));
		if(strcmp(arg,"blu",false)==0 || strcmp(arg,"3",false)==0 ) team=BLU;
		else if(strcmp(arg,"red",false)==0 || strcmp(arg,"2",false)==0 ) team=RED;
		else if(strcmp(arg,"both",false)==0 || strcmp(arg,"0",false)==0 ) team=BOTH_TEAMS;
		else //unknown object team
		{
			ReplyToCommand(client, "%t", "Unknown object team", arg);
			PrintHelp_CtfAdd(client) ;
			return Plugin_Handled;
		}
		
		// #### Copy Defaults
		CopyVector(objpos, g_Def_Pos);
		CopyVector(objang, g_Def_Ang);
		CopyVector(objplrang, g_Def_PlrAng);
		CopyVector(objplrpos, g_Def_PlrPos);
		strcopy(AttachPoint, sizeof(AttachPoint), g_Def_AttachPoint);
		strcopy(Animation, sizeof(Animation), g_Def_Animation);
		
		if(type == g_Type_Flag) strcopy(MyModel, sizeof(MyModel), g_Def_Model[team]);
		else if(type == g_Type_Base) strcopy(MyModel, sizeof(MyModel), g_Def_ModelBase[team]);
		else strcopy(MyModel, sizeof(MyModel), g_Def_ModelBase[RED]); //If other type set something
		
		
		// #### Set object model
		if (args >= 3) 
		{
			GetCmdArg(3, arg, sizeof(arg));
			strcopy(MyModel, sizeof(MyModel), arg);
			PrecacheModel(MyModel);
		}
		// #### Set object Attach Point
		if (args >= 4) 
		{
			GetCmdArg(4, arg, sizeof(arg));
			strcopy(AttachPoint, sizeof(AttachPoint), arg);
		}
		
		// #### Set object pos
		if (args >= 7) 
		{
			GetCmdArg(5, arg, sizeof(arg));
			objpos[0] = StringToFloat(arg);
			GetCmdArg(6, arg, sizeof(arg));
			objpos[1] = StringToFloat(arg);
			GetCmdArg(7, arg, sizeof(arg));
			objpos[2] = StringToFloat(arg);
		}
		else // #### Set object postion on client postition
		{
			if(client <= 0) // Command from rcon/console
			{
				ReplyToCommand(client, "%t", "When using console you must set values");
				PrintHelp_CtfAdd(client);
				return Plugin_Handled;
			}
			GetClientAbsOrigin(client, objpos);
			objpos[0] += g_Def_PosCorect[0];
			objpos[1] += g_Def_PosCorect[1];
			objpos[2] += g_Def_PosCorect[2];
		}
				
		//Copy To Global Setup
		CopyVector(g_ObjPos[g_ObjectsCount], objpos);
		CopyVector(g_ObjAng[g_ObjectsCount], objang);
		CopyVector(g_ObjPlrAng[g_ObjectsCount], objplrang);
		CopyVector(g_ObjPlrPos[g_ObjectsCount], objplrpos);
		g_ObjEnt[g_ObjectsCount]		= 0; //For Flags and base id
		g_ObjType[g_ObjectsCount]		= type; //For Base Ent id
		g_ObjTeam[g_ObjectsCount]		= team;
		strcopy(g_ObjModel[g_ObjectsCount], sizeof(g_ObjModel[]), MyModel);
		strcopy(g_ObjAttachPoint[g_ObjectsCount], sizeof(g_ObjModel[]), AttachPoint);
		strcopy(g_ObjAnimation[g_ObjectsCount], sizeof(g_ObjAnimation[]), Animation);
				
		
		if(client>0) ReplyToCommand(client, "sm_ctf_add \"%s\" \"%d\" \"%s\" \"%s\" %f %f %f", 
			g_ObjectTypes[type], team, g_ObjModel[g_ObjectsCount], g_ObjAttachPoint[g_ObjectsCount],
			g_ObjPos[g_ObjectsCount][0], g_ObjPos[g_ObjectsCount][1], g_ObjPos[g_ObjectsCount][2]
		);
		g_ObjectsCount++;
	}
	
	return Plugin_Handled;
}

#endif

#if 1 //################################### Natives ###############################################################//
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
	RegPluginLibrary("ctfmod_core");
	
	CreateNative("CTF_GetCoreState", Native_GetCoreState);
	CreateNative("CTF_GetMaxObjects", Native_GetMaxObjects);
	CreateNative("CTF_GetObjectsCount", Native_GetObjectsCount);
	CreateNative("CTF_SetObjectsCount", Native_SetObjectsCount);
	CreateNative("CTF_SetObjectParam", Native_SetObjectParam);
	
	
	//Priv Forwards
	CreateNative("CTFHookOnObjectTouch", Hook_OnObjectTouch);
	CreateNative("CTFUnHookOnObjectTouch", UnHook_OnObjectTouch);
	CreateNative("CTFHookObjectCapture", Hook_ObjectCapture);
	CreateNative("CTFUnHookObjectCapture", UnHook_ObjectCapture);
	CreateNative("CTFHookObjectTake", Hook_ObjectTake);
	CreateNative("CTFUnHookObjectTake", UnHook_ObjectTake);
	CreateNative("CTFHookObjectDrop", Hook_ObjectDrop);
	CreateNative("CTFUnHookObjectDrop", UnHook_ObjectDrop);
	
	return APLRes_Success;
}
//native bool:CTF_SetObjectParam(objectnr, type, team, Float:pos[3], Float:ang[3], Float:plrang[3]);
public Native_SetObjectParam(Handle:plugin, numParams)
{
	new objectnr = GetNativeCell(1);
	new type = GetNativeCell(2);
	if(!(type == g_Type_Flag || type == g_Type_Base)) return _:false;
	new team = GetNativeCell(3);
	if(!(team == RED || team == BLU)) return _:false;
	
	
	GetNativeArray(3, g_ObjPos[objectnr], 3);
	GetNativeArray(4, g_ObjAng[objectnr], 3);
	GetNativeArray(5, g_ObjPlrAng[objectnr], 3);
	
	g_ObjType[objectnr]		= type;
	g_ObjTeam[objectnr]		= team;
	return _:true;
}
//native bool:CTF_SetObjectsCount(count);
public Native_SetObjectsCount(Handle:plugin, numParams)
{
	new count = GetNativeCell(1);
	if(count < 0 || count > MAXOBJECTS) return _:false;
	g_ObjectsCount = count;
	return _:true;
}

public Native_GetCoreState(Handle:plugin, numParams)
{
	return g_Enable;
}
//native CTF_GetObjectsCount();
public Native_GetObjectsCount(Handle:plugin, numParams)
{
	return g_ObjectsCount;
}
//native CTF_GetMaxObjects();
public Native_GetMaxObjects(Handle:plugin, numParams)
{
	return MAXOBJECTS;
}

public UnHook_ObjectCapture(Handle:plugin, numParams)
{
	return RemoveFromForward(g_OnObjectCapture, plugin, Function:GetNativeCell(1));
}
public Hook_ObjectCapture(Handle:plugin, numParams)
{
	return AddToForward(g_OnObjectCapture, plugin, Function:GetNativeCell(1));
}
public UnHook_OnObjectTouch(Handle:plugin, numParams)
{
	return RemoveFromForward(g_OnObjectTouch, plugin, Function:GetNativeCell(1));
}
public Hook_OnObjectTouch(Handle:plugin, numParams)
{
	return AddToForward(g_OnObjectTouch, plugin, Function:GetNativeCell(1));
}
public UnHook_ObjectTake(Handle:plugin, numParams)
{
	return RemoveFromForward(g_OnObjectTake, plugin, Function:GetNativeCell(1));
}
public Hook_ObjectTake(Handle:plugin, numParams)
{
	return AddToForward(g_OnObjectTake, plugin, Function:GetNativeCell(1));
}
public UnHook_ObjectDrop(Handle:plugin, numParams)
{
	return RemoveFromForward(g_OnObjectDrop, plugin, Function:GetNativeCell(1));
}
public Hook_ObjectDrop(Handle:plugin, numParams)
{
	return AddToForward(g_OnObjectDrop, plugin, Function:GetNativeCell(1));
}
#endif

#if 1 //################################### Settings Refresh/Setup, Models Precache ###############################//
public MyCvarChange(Handle:convar, const String:oldValue[], const String:newValue[]) //----------- On plugins cvar change
{
	if(strcmp(oldValue, newValue)==0) return; //No change
	RefreshSetting(convar);
	
}

PrecacheAllModels() // ------------ Precache all models
{
	decl String:tmp[MAXCVARLEN];
	//TODO: Precache Loop
	PrecacheModel("sprites/laserbeam.spr");
	for(new i = 0; i < g_ObjectsCount; i++)
	{
		if(g_ObjType[i] <= 0) continue; //Must have type
		
		//Remove models/ prefix
		strcopy(tmp, sizeof(tmp), g_ObjModel[i]);
		ReplaceStringEx(tmp, sizeof(tmp), g_MdlDir, "", -1, -1, false);
		PrecacheModel(tmp);
	}
}


RefreshSetting(Handle:convar=INVALID_HANDLE) //---- Setting state refresh, INVALID_HANDLE == refresh all cvars and precache models
{
	decl bool:boolval;
	if(convar == INVALID_HANDLE || convar == g_Cvar_Enable) 
	{
		boolval = GetConVarBool(g_Cvar_Enable);
		if(boolval!=g_Enable)
		{
			if(boolval)
			{
				TurnOn();
			} 
			else
			{
				TurnOff();
			}
		}
		if(convar != INVALID_HANDLE) return;
	}
	if(convar == INVALID_HANDLE || convar == g_Cvar_Events) 
	{
		boolval = GetConVarBool(g_Cvar_Events);
		if(boolval!=g_Events)
		{
			if(boolval)
			{
				g_Events = true;
			} 
			else
			{
				g_Events = false;
			}
		}
		if(convar != INVALID_HANDLE) return;
	}
	PrecacheAllModels();
}
#endif

#if 1 //################################### TurnOn, TurnOff #######################################################//
TurnOff()
{
	decl Action:result;
	if(!g_Enable) return; // All ready turned off
		
	Call_StartForward(g_TurnOffFwd);
	Call_Finish(_:result);
	
	g_Enable = false;
	PrintToChatAll("[CTFmod %s by KawMAN] %t",PLUGIN_VERSION,"Disabled");
	ServerCommand("exec ctfmod/ctf_off.cfg");
}

TurnOn()
{
	decl Action:result;
	if(g_Enable) return; // All ready turned on
	
	Call_StartForward(g_TurnOnFwd);
	Call_Finish(_:result);
	
	g_Enable = true;
	PrintToChatAll("[CTFmod %s by KawMAN] %t",PLUGIN_VERSION,"Enabled");
	ServerCommand("exec ctfmod/ctf_on.cfg");
}

#endif

#if 1 //################################### Helpers ###############################################################//
//Destroy Object
EntKillH(Ent)
{
	if(IsValidEntity(Ent))
	{
		SetVariantString("");
		AcceptEntityInput(Ent, "KillHierarchy");
		if(!IsValidEntity(Ent)) return Ent;
	}
	return -1;
}

ObjEntToObj(ObjEnt)
{
	return Array_FindValue(g_ObjEnt, g_ObjectsCount, ObjEnt);
}

bool:CanCapture( Obj, ClientObj)
{
	if(ClientObj <= -1) return false; //No Objects to capture
	new ObjTeam = g_ObjTeam[Obj];
	new ObjFlags = g_ObjectTypesF[g_ObjType[Obj]];
	new CObjTeam = g_ObjTeam[ClientObj];
	
	//Check if Object Can Capture Things
	if( 
		( (ObjFlags & CTFTYPE_CAPTURE_TEAM) && (ObjTeam == CObjTeam) ) 		//If Object can capture objects from same team 
		||																		// Or
		( (ObjFlags & CTFTYPE_CAPTURE_OTHERTEAM) && (ObjTeam != CObjTeam) )	//If Object can capture objects from other team
	)
	{
		//Check if Client Object can be captured
		ObjFlags = g_ObjectTypesF[g_ObjType[ClientObj]];
		if(ObjFlags & CTFTYPE_ALLOWCAPTURE) 
		{
			return true;
		}
	}
	return false;
}
bool:CanTouch(client, Obj)
{
	if(IsClientConnected(client) && IsClientInGame(client))
	{
		new ClientTeam = GetClientTeam(client);
		new ObjTeam = g_ObjTeam[Obj];
		new ObjFlags = g_ObjectTypesF[g_ObjType[Obj]];
		if( !(ObjFlags & CTFTYPE_MUST_ALIVE) || ((ObjFlags & CTFTYPE_MUST_ALIVE) && IsPlayerAlive(client)) )
		{
			
			if( 
				( (ObjFlags & CTFTYPE_ALLOWTOUCH_TEAM) && (ObjTeam == ClientTeam) ) 		//If Object allow touch by same team 
				||																			// Or
				( (ObjFlags & CTFTYPE_ALLOWTOUCH_OTHERTEAM) && (ObjTeam != ClientTeam) )	//If Object allow touch by othger team
			)
			{
				return true;
			}
		}
	}
	
	return false;
}
bool:CanTake(client, Obj)
{
	new ClientTeam = GetClientTeam(client);
	new ObjTeam = g_ObjTeam[Obj];
	new ObjFlags = g_ObjectTypesF[g_ObjType[Obj]];
	if( 
		( (ObjFlags & CTFTYPE_ALLOWTAKE_TEAM) && (ObjTeam == ClientTeam) ) 		//If Object allow to be taken by same team 
		||																		// Or
		( (ObjFlags & CTFTYPE_ALLOWTAKE_OTHERTEAM) && (ObjTeam != ClientTeam) )	//If Object allow to be taken by othger team
	)
	{
		//    If allow to take more objects		or			dont have objects
		if( (ObjFlags & CTFTYPE_ALLOWTAKE_MORE) || GetClientObject(client) == -1)
		{
			return true;
		}
	}
	return false;
}

GetClientObject(client)
{
	return Array_FindValue(g_ObjOwner, g_ObjectsCount, client);
}

DetermineWhat(String:name[])
{
	new what = 0;
	if(strcmp(name,"ModelRed",false)==0) 			what=1;
	else if(strcmp(name,"ModelBlu",false)==0) 		what=2;
	else if( (strcmp(name,"Position",false)==0) ||(strcmp(name,"pos",false)==0) ) what=3;
	else if(strcmp(name,"Ang",false)==0)			what=4;
	else if(strcmp(name,"PlrAng",false)==0) 		what=5;
	else if(strcmp(name,"ModelBaseRed",false)==0)	what=6;
	else if(strcmp(name,"ModelBaseBlu",false)==0)	what=7;
	else if(strcmp(name,"AttachPoint",false)==0)	what=8;
	else if(strcmp(name,"PlrPos",false)==0)			what=9;
	else if(strcmp(name,"ModelBoth",false)==0)		what=10;
	else if(strcmp(name,"ModelBaseBoth",false)==0)	what=11;
	else if(strcmp(name,"Anim",false)==0)			what=12;
	else if(strcmp(name,"PosCorect",false)==0)		what=13;
	return what;
}

#endif
